import 'package:flutter/material.dart';
import 'dart:async';

class AsyncCodeDemo extends MaterialPageRoute<Null> {
    AsyncCodeDemo()
        : super(builder: (BuildContext ctx) {
        return Scaffold(
            appBar: AppBar(
                title: Text("异步示例，请参考控制台输出"),
            ),
            body: UI());
    });
}

class UI extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return Column(
            children: <Widget>[
                RaisedButton(
                    child: Text("Future1 demo"),
                    onPressed: future1Demo,
                ),
                RaisedButton(
                    child: Text("Future2 demo"),
                    onPressed: future2Demo,
                ),
                RaisedButton(
                    child: Text("Future3 demo"),
                    onPressed: future3Demo,
                ),
                RaisedButton(
                    child: Text("Stream demo"),
                    onPressed: streamDemo,
                ),
                RaisedButton(
                    child: Text("Micro Task"),
                    onPressed: microTaskDemo,
                ),
                RaisedButton(
                    child: Text("模拟 Eventloop理解系统机制，此例可忽略"),
                    onPressed: eventLoopDemo,
                ),
                RaisedButton(
                    child: Text("Dart sub Zone catch Error Demo"),
                    onPressed: subZoneDemo,
                ),
            ],
        );
    }
    
    future1Demo() {
        print("future1Demo button clicked!");
        
        loadString1("demo1").then((onValue) => print(onValue));
        
        sameAsloadString1("demo   1").then((onValue) {
            print(onValue);
        });
    }
    
    Future<String> loadString1(String str) {
        return Future.delayed(Duration(seconds: 1)).then((____) { //____是自定义的形参名而已
            return "String $str";
        });
    }
    
    Future<String> sameAsloadString1(String str) {
        Future f = new Future.delayed(Duration(seconds: 1)); //延迟1秒"发射"字符串
        
        return f.then((____) {
            return "String $str";
        });
    }
    
    future2Demo() {
        print("future2Demo button clicked!");
        callDemo2();
    }
    
    void callDemo2() async {
        String str = await loadString2("demo 2");
        print(str);
    }
    
    Future<String> loadString2(String str) async {
        await Future.delayed(Duration(seconds: 1)); //延迟1秒的另一种方式
        return "String $str";
    }
    
    future3Demo() {
        print("future3Demo button clicked!");
        
        addAsync(20) //注意addAsync的返回值是个函数，此次调用返回的匿名函数:形参为y,函数体为20+y
            .then(
                (s) => s(50), //调用上面那个函数，将50传给y
        )
            .then(
                (_) => print(_), //打印那个函数的返回值 70
        );
        
        anotherWayCallAddAsync(); //上面链式调用不好理解的话看这个方式
    }
    
    void anotherWayCallAddAsync() async {
        Function aaa = await addAsync(10); //注意addAsync的返回值是个函数，此次调用返回的匿名函数:形参为y,函数体为10+y。并将该匿名函数赋予名字aaa
        int res = await aaa(80);
        print(res);
    }
    
    Future<Function> addAsync(int x) async {
        return (int y) async { //类似于函数指针
            await Future.delayed(Duration(seconds: 1));
            return x + y;
        };
    }
    
    void streamDemo() {
        print("streamDemo button clicked!");
        
        callDemoSteam();
        callDemoSteamWay2();
    }
    
    void callDemoSteam() async {
        Stream stream = demoStream(5, false);
        await for (var s in stream) { //await for方式从流中取数据
            print(s);
        }
    }
    
    void callDemoSteamWay2() async {
        demoStream(5, true).listen((onData) => print("num:$onData")); //listen方式从流中取数据
    }
    
    Stream<int> demoStream(int x, bool delay) async* {
        //构造一个数据流
        for (var i = 0; i < x; i++) {
            if (delay) {
                await Future.delayed(Duration(seconds: 1));
            }
            yield i;
        }
    }
    
    void microTaskDemo() {
        print("microTaskDemo Button clicked");
        callMicroTask();
    }
    
    void callMicroTask() async {
        //异步任务优先级！！！请重点关注控制台输出顺序
        print("start");
        
        Future.delayed(Duration(seconds: 1), () => print("Future 1"));
        
        Timer(
            Duration(seconds: 2), () {
            print("executed Timer 1");
        });
        
        Timer.run( //等同Duration.zero
                () {
                print("executed Timer 2");
            });
        
        scheduleMicrotask(() => print("micro task 1"));
        
        Future.delayed(Duration(seconds: 3), () => print("Future 2"));
        
        scheduleMicrotask(() => print("micro task 2"));
        
        print("end");
    }
    
    eventLoopDemo() {
        print("eventloopDemo Button clicked");
        EventLoop.call(fakeDartMain, []);
    }
    
    void fakeDartMain(List<String> args) {
        //模拟dart的main函数里的代码
        int x = 5; //声明变量
        _runFunc() {
            //声明函数
            x--;
            if (x != 0) {
                print("x:$x");
                
                //以下3行代码只能放开一行；
//                Timer(Duration(), _runFunc);//Timer方式
                Future(_runFunc); //Future方式
//                scheduleMicrotask(_runFunc()); //Microtask方式 scheduleMicrotask会报错
                
            }
        }
        
        _runFunc(); //调用函数
    }
    
    subZoneDemo() {
        runZoned(
                () async {
                runThrowError(); //在sub zone里抛异常
            },
            zoneSpecification: ZoneSpecification( //
                print: ( //
                    Zone self, //
                    ZoneDelegate parent, //
                    Zone zone, //
                    String message) {
                    parent.print(zone, "change sub zone 's print behiver:$message");
                }), //
            onError: (e, stackTrace) {
                print(e.toString()); //这里的print行为没有被改变是因为，只改变的是body里的
                
            }
        );
        
        runWithoutError(); //这一行的行为显示了，说明subzone出错了也不影响rootzone
    }
    
    Future runThrowError() async {
        Stream steam = createEvent();
        await for (var x in steam) {
            print("when x=9 error, x:$x");
            if (x == 9) { //当到达事件9时手动抛个异常
                throw ("error by cjz");
            }
        }
    }
    
    Future runWithoutError() async {
        Stream steam = createEvent();
        await for (var x in steam) {
            print("no error x:$x");
        }
    }
    
    //产生一个异步事件流用于测试
    Stream<int> createEvent() async* {
        for (var i = 0; i < 20; i++) {
            yield i;
        }
    }
}

class EventLoop {
    static final List<Function> queue = [];
    static final List<Function> microTask = [];
    
    static void run(Function f) {
        print("queue");
        queue.add(f);
    }
    
    static void runMicroTask(Function f) {
        print("EventLoop run a MicroTask");
        microTask.add(f);
    }
    
    static void eventLoop() {
        //模拟dart内部的loop 为什么micro task优先
        while (queue.isNotEmpty) {
            while (microTask.isNotEmpty) {
                microTask.removeAt(0)(); //注意这里有2个括号，取出即调用
            }
            queue.removeAt(0)(); //注意这里有2个括号，取出即调用
        }
        
        if (microTask.isNotEmpty || queue.isNotEmpty) {
            eventLoop();
        }
    }
    
    static void call(Function func, List<String> args) {
        var zone = Zone.current.fork(
            
            specification: ZoneSpecification(
                
                scheduleMicrotask: (zone1, delegate, zone2, func) { //micro task交给scheduleMicrotask
                    EventLoop.runMicroTask(func);
                },
                
                createTimer: (zone1, delegate, zone2, duration, func) { //future 和 timer交给createTimer
                    if (duration == Duration.zero) {
                        EventLoop.run(func);
                        return null;
                    } else {
                        return Timer(duration, func);
                    }
                })
        );
        zone.runUnary(func, args);
        eventLoop();
    }
}
